structure MipsFrame : FRAME =
struct
    structure A = Assem
    structure E = ErrorMsg
    structure M = Temp
    structure R = Tree
    structure S = Symbol

    type register = string

    val FP = Temp.newtemp ()
    val FPreg = ("fp", FP)
    
    val RA = Temp.newtemp ()
    val RAreg = ("ra", RA)

    val RV = Temp.newtemp ()
    val RVreg = ("rv", RV)

    val SP = Temp.newtemp ()
    val SPreg = ("rp", SP)
		
    val ZERO = Temp.newtemp ()
    val ZEROreg = ("zero", ZERO)

    fun makeReg (s, n) = (s ^ (Int.toString n), Temp.newtemp ())

    fun makeRegs (s, n) = if (0=n)
			  then [makeReg (s, 0)]
			  else (makeReg (s, n))::makeRegs (s, n-1)

    val spregs = [ ZEROreg, FPreg, RAreg, RVreg, SPreg ]
    val specialregs = map (fn (s, _) => s) spregs (* gp, at, k0, k1 *)

    val aregs = makeRegs ("a", 3)
    val argregs = map (fn (s, _) => s) aregs

    val esregs = makeRegs ("s", 7)
    val estemps = map (fn (_, t) => t) esregs
    val calleesaves = map (fn (s, _) => s) esregs

    val rsregs = (makeRegs ("t", 9)) @ [makeReg ("v", 0)]
    val callersaves = map (fn (s, _) => s) rsregs
		      
    val registers = specialregs @ argregs @ calleesaves @ callersaves
		      
    val tempMap = foldr (fn ((s, m), t) => M.Table.enter (t, m, s))
			M.Table.empty
			(spregs @ aregs @ esregs @ rsregs)
		  
    fun temp2string t = case (M.Table.look (tempMap, t))
			 of NONE => M.makestring t
			  | SOME s => s

    datatype access = InFrame of int
		    | InReg of M.temp

    (*
     * The first 4 parameters are passed in registers, with remaining ones passed on the stack
     *
     * The stack layout is
     *
     *  outgoing args                                 incoming args    older frames ...
     *                 temps | ret | locals | static parm ... parm | ...
     *               ^                      ^ 
     *              SP                     FP
     *  ... lower addresses ...................................... higher addresses ...
     *)

    type frame = { name : M.label,
		   formals : access list,
		   sp : int ref }

    val wordSize = 4

    fun newFrame {name, formals} = let val maxIncomingRegs = 4
				       val firstIncomingOffset = wordSize
				       fun placeFormal (_, (n, 0, formals)) = (n+wordSize, 0, (InFrame n)::formals)
					 | placeFormal (true, (n, r, formals)) = (n+wordSize, r, (InFrame n)::formals)
					 | placeFormal (false, (n, r, formals)) = (n, r-1, (InReg (M.newtemp())::formals))
				   in let val (_, _, formals) = foldl placeFormal (firstIncomingOffset, maxIncomingRegs, []) formals
				      in {name = name, formals = formals, sp = ref 0}
				      end
				   end
				   
    fun formals {name, formals=formals, sp} = formals
	
    fun name {name=name, formals, sp} = name
	
    fun postdec r = let val n = !r
		    in ( r := n - wordSize
		       ; n )
		    end
		    
    fun allocLocal {name, formals, sp=sp} true = InFrame (postdec sp)
      | allocLocal _ false = InReg (M.newtemp ())
			     
    fun externalCall (s, args) = R.CALL (R.NAME (Temp.namedlabel ("_" ^ s)), args)

    (* yields an R.MEM or R.TEMP which can be used as an l-value or an r-value *)
    fun exp (InFrame k) fp = R.MEM (R.BINOP (R.PLUS, fp, (R.CONST k)))
      | exp (InReg r) _ = R.TEMP r

    fun staticLink {name,formals=[],sp} = E.impossible "NO STATIC LINK"
      | staticLink {name,formals=(sl::realFormals),sp} = sl


    fun procEntryExit1 (frame, stm) = stm (* dummy as on p.170 *)

    fun procEntryExit2 (frame, body) = body @ [A.OPER { assem = "", (* cf. pp. 208--209 *)
							src=[ZERO, RA, SP, FP]@estemps,
							dst=[],
							jump=SOME[] }]

    fun procEntryExit3 ({name, formals, sp}, body) = (* dummy on p.209 *)
	{ prolog = "#PROCEDURE " ^ S.name name ^ "\n",
	  body=body,
	  epilog = "#END " ^ S.name name ^ "\n" }
	
    fun string (l, s) = (S.name l) ^ ": .asciiz " ^ "\"" ^ (String.toCString s) ^ "\"" ^ "\n"



    datatype frag = PROC of {body : Tree.stm, frame : frame }
		  | STRING of Temp.label * string
end
